# -*- coding: utf-8 -*-
'''
value: 默认值
'''
import json
import os

from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtGui import QBrush, QColor, QIntValidator, QDoubleValidator, QRegExpValidator, QValidator,QPainter
from PyQt5.QtCore import Qt,QRegExp, pyqtSignal, pyqtSlot
from PyQt5.QtWidgets import (
    QHeaderView,QAbstractItemView,QLabel,QLineEdit,QWidget, 
    QTableWidget, QPushButton, QFileDialog, QHBoxLayout, 
    QTableWidgetItem,QApplication,QDialog,QCheckBox,QMessageBox,QComboBox
)

from .ui_custom_combo_box import UiCustomComboBox
from .comboCheckBox import ComboCheckBox
from .tooltip import ToolTip

import gui.controls.apis as apis


global  CELL_HEIGHT
CELL_HEIGHT = 30
def createLayout():
    layout = QHBoxLayout()
    layout.setSpacing(5)
    layout.setContentsMargins(10,0,10,0)
    # 设置背景色
    return layout
def getWidgetHeight():
    global  CELL_HEIGHT
    return CELL_HEIGHT
def getCellBtnWidth():
    return 60

class TableWidgetItemRowHeader(QTableWidgetItem):
    def __init__(self, item:dict={}, *args, groupLine=False, **kwargs):
        name = item.get('name')
        title = item.get('title')
        titleAlign = item.get('titleAlign')
        titleToggle = item.get('titleToggle')
        super(TableWidgetItemRowHeader, self).__init__(title or name or '')
        if groupLine:
            self.setBackground(QColor(100, 116, 139))
            self.setForeground(QColor(255, 255, 255))
            font = QtGui.QFont()
            font.setBold(True)
            font.setPointSize(14)
            self.setFont(font)
        else:
            self.setBackground(QColor(240, 240, 240))
        self.setData(QtCore.Qt.UserRole, item)
        if titleAlign == 'center':
            self.setTextAlignment(Qt.AlignCenter)
        elif titleAlign == 'right':
            self.setTextAlignment(Qt.AlignVCenter | Qt.AlignRight)

        if titleToggle:
            self.setFlags(self.flags() | QtCore.Qt.ItemIsUserCheckable)
        else:
            self.setFlags(self.flags() & ~QtCore.Qt.ItemIsUserCheckable)
# 显示文本按钮， 一般用于多维数组
class TextChangedButton(QPushButton):
    textChanged = QtCore.pyqtSignal(str)
    def __init__(self, placeholder_text, parent=None):
        super().__init__()
        self.placeholder_text = placeholder_text
        self.clicked.connect(self.clearPlaceholderText)
        self.setFixedHeight(getWidgetHeight())
        self.setStyleSheet("""
            QPushButton {
                width:100%;
                height:100%;
                margin:0;
            }
        """)
    def setText(self, text):
        if self.text() != text:
            self.setToolTip(text)
            super().setText(text)
            self.textChanged.emit(text)
    def paintEvent(self, event):
        super().paintEvent(event)
        if not self.text():
            painter = QPainter(self)
            painter.setPen(QColor("gray"))
            painter.drawText(self.rect(), Qt.AlignLeft | Qt.AlignVCenter, ' ' + self.placeholder_text)
    def clearPlaceholderText(self):
        if self.text() == self.placeholder_text:
            self.setText("")
# 自定义 QLineEdit 主要处理修改事件
class CusChangedQLineEdit(QLineEdit):
    def __init__(self, parent=None):
        super().__init__()
        self.parent = parent
        self.table = parent.table
        self.data = {}
        self.textChanged.connect(self.on_textChanged)
    def _init(self, data:dict={}):
        self.data = data
        self.setPlaceholderText(data.get('placeholder') or '')
        # 判断是否有 range 属性 因为range属性必然是数值类型 需要进行校验并提示
    def on_textChanged(self):
        key = self.data.get('name')
        value = self.parent._get()
        if 'range' in self.data:
            isError = self.text_range_warning(self.data['range'], value)
            if isError: return
        if hasattr(self.table, 'change_cache_key_value'):
            self.table.change_cache_key_value(key, value)
    # 范围校验 range：[None ,1] None 表示没有最小限制
    def text_range_warning(self, range, value):
        if value == '' or value == None: 
            return 
        # 判断 范围
        if len(range) < 2: # range 的长度至少有两个 0：最小值 1：最大值， 3：是否包含最小 4：是否包含最大 （3，4 可以不设置默认都包含True）
            return 
        hasMin = True 
        hasMax = True 
        if len(range) == 4:
            hasMin = range[2] == True
            hasMax = range[3] == True
        elif len(range) == 3:
            hasMin = range[2] == True
        min = range[0]
        max = range[1]

        isError = False
        if min == None:
            if max == None: return 
            if hasMax and value > max:
                msg = f"请输入小于等于{max}的数值"
                isError = True
            elif not hasMax and value >= max:
                msg = f"请输入小于{max}的数值"
                isError = True
        elif max == None:
            if min == None: return 
            if hasMin and value < min:
                msg = f"请输入大于等于{min}的数值"
                isError = True
            elif not hasMin and value <= min:
                msg = f"请输入大于{min}的数值"
                isError = True
        else:
            if hasMax and value > max:
                msg = f"请输入小于等于{max}的数值"
                isError = True
            elif not hasMax and value >= max:
                msg = f"请输入小于{max}的数值"
                isError = True
            elif hasMin and value < min:
                msg = f"请输入大于等于{min}的数值"
                isError = True
            elif not hasMin and value <= min:
                msg = f"请输入大于{min}的数值"
                isError = True
        if isError :
            QMessageBox.warning(self, 'Error', msg)
            self.selectAll()#选择全部内容
            self.setFocus()#获得焦点
            self.setText('')
            return isError
# 自定义 checkbox
class CusChangedQCheckBox(QCheckBox):
    def __init__(self, parent=None):
        super().__init__()
        self.parent = parent
        self.table = parent.table
        self.data = {}
        self.stateChanged.connect(self.on_changed)
        # 注意 存在一种情况就是 初值和默认值是一样的 这样就会造成状态不会发生变化，导致不会onChanged 事件
    def _init(self, data:dict={}):
        self.data = data
        onInit = self.data.get('onInit')
        if isinstance(onInit, type(lambda x:x)): # 存在初始化函数则初始化 自动执行
            updateAfterContent = lambda rows:self._update_after_content(rows)
            updateValues = lambda rows:self._update_values(rows)
            # 缺省默认为False
            onInit(data.get('value', False), updateAfterContent=updateAfterContent, updateValues=updateValues)

    def on_changed(self, state):
        key = self.data.get('name')
        value = self.parent._get()
        onChanged = self.data.get('onChanged')
        if isinstance(onChanged, type(lambda x:x)):
            updateAfterContent = lambda rows:self._update_after_content(rows)
            updateValues = lambda rows:self._update_values(rows)
            onChanged(value, updateAfterContent=updateAfterContent, updateValues=updateValues)
        if hasattr(self.table, 'change_cache_key_value'):
            self.table.change_cache_key_value(key, value)
    def do_nothing(self):
        pass
    # 更新后续内容，一般用于切换时需要动态变化后续的表格行
    def _update_after_content(self, afterRows:list=[]):
        rowIndex = self.data.get('rowIndex', 0)
        # 需要动态更新后续的单元格信息
        self.table._append_rows(rowIndex, afterRows)
    def _update_values(self, values:dict={}):
        # 需要动态更新后续的单元格信息
        if values:
            self.table._update_values(values)
# 单元格-字符串
class CustomCellStr(QWidget):
    def __init__(self, parent, *args, **kwargs):
        super(CustomCellStr, self).__init__(*args, **kwargs)
        self.table = parent
        self.data = {}
        layout = createLayout()
        self.widget = CusChangedQLineEdit(self)
        layout.addWidget(self.widget)
        self.widget.setClearButtonEnabled(True)
        self.setLayout(layout)
        self.widget.setFixedHeight(getWidgetHeight())
    # 初始化必要信息
    def _init(self, data:dict={}):
        self.data = data
        self.widget._init({'placeholder':"字符串", **data})
    def _set(self,value):
        self.widget.setText(value != None and str(value) or '')
    def _get(self):
        return self.widget.text() or None

# 单元格-路径字符串 带btn
class CustomCellPath(QWidget):
    def __init__(self, parent,*args, **kwargs):
        super(CustomCellPath, self).__init__(*args, **kwargs)
        self.table = parent
        self.data = {}
        layout = createLayout()
        self.widget = CusChangedQLineEdit(self)
        layout.addWidget(self.widget)
        self.widget.setPlaceholderText('路径')
        self.widget.setClearButtonEnabled(True)
        self.btn = QPushButton('...')
        self.btn.setFixedWidth(getCellBtnWidth())
        self.btn.clicked.connect(self.on_click)
        layout.addWidget(self.btn)
        self.setLayout(layout)
        self.widget.setFixedHeight(getWidgetHeight())
    # 初始化必要信息
    def _init(self, data:dict={}):
        self.data = data
        self.widget._init({'placeholder':"磁盘目录", **data})
    def _set(self,value):
        self.widget.setText(value != None and str(value) or '')
    def _get(self):
        return self.widget.text() or None
    def on_click(self, *args, **kwargs):
        # QFileDialog 选择路径
        dir = QFileDialog.getExistingDirectory(self, "选择路径", "data/")
        if not dir or dir == "":
            return 
        self.widget.setText(dir.replace('\\','/'))
        onOpened = self.data.get('onOpened')
        # 选择路径 文件后可能需要 对变更信息进行解析更新相关数据
        if isinstance(onOpened, type(lambda x:x)):
            updateValues = lambda rows:self._update_values(rows)
            onOpened(self._get(), updateValues=updateValues)
    def _update_values(self, values:dict={}):
        # 需要动态更新后续的单元格信息
        if values:
            self.table._update_values(values)
# 单元格-路径字符串 vtkGroup
class CustomCellVtkGroup(QWidget):
    def __init__(self, parent,*args, **kwargs):
        super(CustomCellVtkGroup, self).__init__(*args, **kwargs)
        self.table = parent
        self.data = {}
        layout = createLayout()
        self.widget = CusChangedQLineEdit(self)
        layout.addWidget(self.widget)
        self.widget.setPlaceholderText('路径')
        self.widget.setClearButtonEnabled(True)

        self.btnOpen = QPushButton('显示')
        self.btnOpen.setFixedWidth(getCellBtnWidth())
        self.btnOpen.clicked.connect(self.on_show_vtkgroup)
        layout.addWidget(self.btnOpen)

        self.btn = QPushButton('...')
        self.btn.setFixedWidth(getCellBtnWidth())
        self.btn.clicked.connect(self.on_click)
        layout.addWidget(self.btn)
        self.setLayout(layout)
        self.widget.setFixedHeight(getWidgetHeight())
    def on_show_vtkgroup(self):
        path = self.widget.text().strip()
        if path and os.path.exists(path):
            apis.open_vtk_group(os.path.abspath(path))
    # 初始化必要信息
    def _init(self, data:dict={}):
        self.data = data
        self.widget._init({'placeholder':"磁盘目录", **data})
    def _set(self,value):
        self.widget.setText(value != None and str(value) or '')
    def _get(self):
        return self.widget.text() or None
    def on_click(self, *args, **kwargs):
        # QFileDialog 选择路径
        dir = QFileDialog.getExistingDirectory(self, "选择文件夹", "data/")
        if not dir or dir == "":
            return 
        self.widget.setText(dir.replace('\\','/'))
        onOpened = self.data.get('onOpened')
        # 选择路径 文件后可能需要 对变更信息进行解析更新相关数据
        if isinstance(onOpened, type(lambda x:x)):
            updateValues = lambda rows:self._update_values(rows)
            onOpened(self._get(), updateValues=updateValues)
    def _update_values(self, values:dict={}):
        # 需要动态更新后续的单元格信息
        if values:
            self.table._update_values(values)
# 单元格-文件字符串 带btn
class CustomCellFile(QWidget):
    def __init__(self, parent,*args, **kwargs):
        super(CustomCellFile, self).__init__(*args, **kwargs)
        self.table = parent
        self.data = {}
        layout = createLayout()
        self.widget = CusChangedQLineEdit(self)
        layout.addWidget(self.widget)
        self.widget.setPlaceholderText('文件')
        self.widget.setClearButtonEnabled(True)
        self.btn = QPushButton('...')
        self.btn.setFixedWidth(getCellBtnWidth())
        self.btn.clicked.connect(self.on_click)
        layout.addWidget(self.btn)
        self.setLayout(layout)
        self.widget.setFixedHeight(getWidgetHeight())
    def _init(self, data:dict={}):
        self.data = data
        self.widget._init({'placeholder':"磁盘文件", **data})

    def _set(self,value):
        self.widget.setText(value != None and str(value) or '')
    def _get(self):
        return self.widget.text() or None
    def on_click(self, *args, **kwargs):
        # QFileDialog 选择磁盘文件
        text = self.widget.text()
        (file, fileType) = QFileDialog.getOpenFileName(self, "选取文件", text or 'data/', "All Files (*);;")
        if not file or file == "":
            return 
        self.widget.setText(file.replace('\\','/'))
        onOpened = self.data.get('onOpened')
        # 选择路径 文件后可能需要 对变更信息进行解析更新相关数据
        if isinstance(onOpened, type(lambda x:x)):
            updateValues = lambda rows:self._update_values(rows)
            onOpened(self._get(), updateValues=updateValues)
    def _update_values(self, values:dict={}):
        # 需要动态更新后续的单元格信息
        if values:
            self.table._update_values(values)
# model file 
class CustomCellModelFile(QWidget):
    fileType = 'text'
    def __init__(self, parent, *args, **kwargs):
        super(CustomCellModelFile, self).__init__(*args, **kwargs)
        self.table = parent
        self.data = {}
        
        layout = createLayout()
        self.widget = CusChangedQLineEdit(self)
        layout.addWidget(self.widget)
        self.widget.setPlaceholderText('文件')
        self.widget.setClearButtonEnabled(True)
        self.btnOpen = QPushButton('显示')
        self.btnOpen.setFixedWidth(getCellBtnWidth())
        self.btnOpen.clicked.connect(self.on_show_view)
        layout.addWidget(self.btnOpen)

        self.btnFile = QPushButton('...')
        self.btnFile.setFixedWidth(getCellBtnWidth())
        self.btnFile.clicked.connect(self.on_click)
        layout.addWidget(self.btnFile)
        self.setLayout(layout)
        self.widget.setFixedHeight(getWidgetHeight())
    def _init(self, data:dict={}):
        self.data = data
        self.widget._init({'placeholder':f"{self.fileType.upper()}文件", **data})
    def _set(self,value):
        self.widget.setText(value != None and str(value) or '')
    def _get(self):
        return self.widget.text() or None
    def on_show_view(self):
        file = self.widget.text().strip()
        if file and os.path.exists(file):
            self.open_file(file)
            from gui.config import InsConfigIndex
            InsConfigIndex().show_def_bgcolor()

    def on_click(self, *args, **kwargs):
        # QFileDialog 选择磁盘文件
        text = self.widget.text()
        (file, fileType) = QFileDialog.getOpenFileName(self, "选取文件", text or 'data/', f"INP Files (*.{self.fileType});;All Files (*)")
        if not file or file == "":
            return 
        self.widget.setText(file.replace('\\','/'))
        onOpened = self.data.get('onOpened')
        # 选择路径 文件后可能需要 对变更信息进行解析更新相关数据
        if isinstance(onOpened, type(lambda x:x)):
            updateValues = lambda rows:self._update_values(rows)
            onOpened(self._get(), updateValues=updateValues)
    def _update_values(self, values:dict={}):
        # 需要动态更新后续的单元格信息
        if values:
            self.table._update_values(values)
    def open_file(self, file):
        print('open file -> ', file)
# INP 文件
class CustomCellInpFile(CustomCellModelFile):
    fileType = 'inp'
    def __init__(self, parent, *args, **kwargs):
        super(CustomCellInpFile, self).__init__(parent, *args, **kwargs)
    def open_file(self, file):
        apis.open_inp(file)
# BDF 文件
class CustomCellBdfFile(CustomCellModelFile):
    fileType = 'bdf'
    def __init__(self, parent,*args, **kwargs):
        super(CustomCellBdfFile, self).__init__(parent,  *args, **kwargs)
    def open_file(self, file):
        print('open BDF file -> ', file)
        # apis.addBdfModel(file)
        # apis.pyd_show_model(file)
# SDB 文件
class CustomCellSdbFile(CustomCellModelFile):
    fileType = 'sdb'
    def __init__(self, parent,*args, **kwargs):
        super(CustomCellSdbFile, self).__init__(parent, *args, **kwargs)
    def open_file(self, file):
        print('open SDB file -> ', file)
        # apis.addSdbModel(file)
        # apis.pyd_show_model(file)
# VTK 文件
class CustomCellVtkFile(CustomCellModelFile):
    fileType = 'vtk'
    def __init__(self, parent,*args, **kwargs):
        super(CustomCellVtkFile, self).__init__(parent, *args, **kwargs)
    def open_file(self, file):
        apis.open_vtk(file)
        # apis.addVtkModel(file)
        # apis.pyd_show_model(file)
# VTU 文件
class CustomCellVtuFile(CustomCellModelFile):
    fileType = 'vtu'
    def __init__(self, parent,*args, **kwargs):
        super(CustomCellVtuFile, self).__init__(parent,  *args, **kwargs)
    def open_file(self, file):
        apis.open_vtu(file)
        # apis.addVtuModel(file)
        # apis.pyd_show_model(file)
# # DSP 文件
# class CustomCellDspFile(CustomCellModelFile):
#     fileType = 'dsp'
#     def __init__(self, parent,*args, **kwargs):
#         super(CustomCellDspFile, self).__init__(parent,  *args, **kwargs)
#     def open_file(self, file):
#         from gui.dsp import Dsp
#         curDsp = Dsp()
#         curDsp.set_temp()
#         curDsp.import_dsp(file)
#         curDsp.open_dsp()
#         curDsp.set_cur()
#         curDsp.show_model()
#         curDsp.remove_dsp()
#         curDsp.close()
        
# VTR 文件
class CustomCellVtrFile(CustomCellModelFile):
    fileType = 'vtr'
    def __init__(self, parent,*args, **kwargs):
        super(CustomCellVtrFile, self).__init__(parent,  *args, **kwargs)
    def open_file(self, file):
        print('open VTR file -> ', file)
        # apis.addVtrModel(file)
        # apis.pyd_show_model(file)

class CustomCellBatFile(CustomCellModelFile):
    fileType = 'bat'
    def __init__(self, parent,*args, **kwargs):
        super(CustomCellBatFile, self).__init__(parent,  *args, **kwargs)
    def open_file(self, file):
        os.system(f"start {file}")

# 单元格-布尔值
class CustomCellBool(QWidget):
    def __init__(self, parent,*args, **kwargs):
        super(CustomCellBool, self).__init__(*args, **kwargs)
        self.table = parent
        self.data = {}
        layout = createLayout()
        self.widget = CusChangedQCheckBox(self)
        layout.addWidget(self.widget)
        self.setLayout(layout)
        self.widget.setFixedHeight(getWidgetHeight())
    def _init(self, data:dict={}):
        self.data = data
        self.widget._init(data)
    def _set(self,value):
        self.widget.setCheckState(value and Qt.Checked or Qt.Unchecked)
    def _get(self):
        if Qt.Checked == self.widget.checkState():
            return True 
        else:
            return False

# 单元格-布尔值 带标题类似于 checkbox 组件
class CustomCellCheckbox(QWidget):
    def __init__(self, parent,*args, **kwargs):
        super(CustomCellCheckbox, self).__init__(*args, **kwargs)
        self.table = parent
        self.data = {}
        layout = createLayout()
        self.widget = CusChangedQCheckBox(self)
        layout.addWidget(self.widget)
        self.checkboxTitleWidget = QLabel()
        layout.addWidget(self.checkboxTitleWidget)
        layout.addStretch(1)
        self.setLayout(layout)
        self.widget.setFixedHeight(getWidgetHeight())
    def _init(self, data:dict={}):
        self.data = data
        self.widget._init(data)
        self.checkboxTitleWidget.setText(data.get('checkboxTitle') or '')
    def _set(self,value):
        self.widget.setCheckState(value and Qt.Checked or Qt.Unchecked)
    def _get(self):
        if Qt.Checked == self.widget.checkState():
            return True 
        else:
            return False
# 单元格-整形
class CustomCellInt(QWidget):
    def __init__(self, parent,*args, **kwargs):
        super(CustomCellInt, self).__init__(*args, **kwargs)
        self.table = parent
        self.data = {}
        layout = createLayout()
        self.widget = CusChangedQLineEdit(self)
        self.widget.setPlaceholderText('整数')
        self.widget.setClearButtonEnabled(True)

        validator = QIntValidator()
        self.widget.setValidator(validator)

        layout.addWidget(self.widget)
        self.setLayout(layout)
        self.widget.setFixedHeight(getWidgetHeight())
    def _init(self, data:dict={}):
        self.data = data
        self.widget._init({'placeholder':"整数", **data})
    def _set(self,value):
        if value == None:
            value = ''
        self.widget.setText(str(value))
    def _get(self):
        value = self.widget.text()
        if value != '' :
            try:
                return int(value)
            except ValueError:
                print("输入的不是一个数字字符串")
        return None
    

    
# 单元格-浮点型
class CustomCellFloat(QWidget):
    def __init__(self, parent,*args, **kwargs):
        super(CustomCellFloat, self).__init__(*args, **kwargs)
        self.table = parent
        layout = createLayout()
        self.widget = CusChangedQLineEdit(self)
        self.widget.setPlaceholderText('浮点数')
        self.widget.setClearButtonEnabled(True)
        validator = QDoubleValidator()
        self.widget.setValidator(validator)

        layout.addWidget(self.widget)
        self.setLayout(layout)
        self.widget.setFixedHeight(getWidgetHeight())
    def _init(self, data:dict={}):
        self.data = data
        self.widget._init({'placeholder':"浮点数", **data})
    def _set(self, value:float=None):
        if value == None:
            value = ''
        self.widget.setText(str(value))
    def _get(self):
        value = self.widget.text()
        if value != '' :
            try:
                return float(value)
            except ValueError:
                print("输入的不是一个数字字符串")
        return None
class CustomCellComboBoxSelect(QWidget):
    def __init__(self, parent, *args, **kwargs):
        super(CustomCellComboBoxSelect, self).__init__(*args, **kwargs)
        self.parent = parent
        self.table = parent.table
        layout = createLayout()
        self.widget = UiCustomComboBox()
        layout.addWidget(self.widget)
        self.setLayout(layout)
        self.widget.setFixedHeight(getWidgetHeight())
        self.widget.currentTextChanged.connect(lambda: self._on_currentTextChanged())
    def _init(self, data:dict={}):
        self.data = data
        cacheValue= self.table.get_cache_key_value(self.data.get('name'))
        self._init_select_options(data.get('data') or data.get('onInitData'))
        if cacheValue != None:
            """因此为一旦重新初始化则可能导致数据修改,丢失以缓存的数据,变成原来的样子,因此需要判断cacheValue是否为空初始化数据"""
            self._set(cacheValue)

    def _init_select_options(self, data):
        # 注意 data 可能是数组 也可能是函数
        items = []
        if isinstance(data, list):
            items = data 
        elif isinstance(data, type(lambda x:x)) :
            items = data()
        self.widget._init(items)
    # 当选项发生变化后需要执行的动作
    def _on_currentTextChanged(self):
        # 更新缓存数据
        # 检查切换函数
        onChanged = self.data.get('onChanged')
        if isinstance(onChanged, type(lambda x:x)):
            updateAfterContent = lambda rows:self._update_after_content(rows)
            updateValues = lambda rows:self._update_values(rows)
            onChanged(self.widget._get(), updateAfterContent=updateAfterContent, updateValues=updateValues)
        if  hasattr(self.table, 'change_cache_key_value'):
            self.table.change_cache_key_value(self.data.get('name'), self._get())
    # 更新后续内容，一般用于切换时需要动态变化后续的表格行
    def _update_after_content(self, afterRows:list=[]):
        rowIndex = self.data.get('rowIndex', 0)
        # 需要动态更新后续的单元格信息
        self.table._append_rows(rowIndex, afterRows)
    def _update_values(self, values:dict={}):
        # 需要动态更新后续的单元格信息
        if values:
            self.table._update_values(values)
# 单元格-下拉选择框 选择字符串
class CustomCellComboBoxSelectStr(CustomCellComboBoxSelect):
    def __init__(self, parent,*args, **kwargs):
        self.table = parent
        super(CustomCellComboBoxSelectStr, self).__init__(self, *args, **kwargs)
    def _get(self):
        return self.widget._get()
    def _set(self, value:str=None):
        return self.widget._set(value)
# 单元格-下拉选择框 选择数值
class CustomCellComboBoxSelectInt(CustomCellComboBoxSelect):
    def __init__(self, parent,*args, **kwargs):
        self.table = parent
        super(CustomCellComboBoxSelectInt, self).__init__(self, *args, **kwargs)
    def _get(self):
        val = self.widget._get()
        if val == '':
            return None
        return int(val)
    def _set(self, value:int=None):
        return self.widget._set(str(value))
    
#  单元格-下拉选择框 选择浮点数
class CustomCellComboBoxSelectFloat(CustomCellComboBoxSelect):
    def __init__(self, parent,*args, **kwargs):
        self.table = parent
        super(CustomCellComboBoxSelectFloat, self).__init__(self, *args, **kwargs)
    def _get(self):
        val = self.widget._get()
        if val == '':
            return None
        return float(val)
    def _set(self, value:float=None):
        return self.widget._set(str(value))

# 单元格-下拉选择框 值是数组 且数组中目前仅有一项
class CustomCellComboBoxSelectListStr(CustomCellComboBoxSelect):
    def __init__(self, parent,*args, **kwargs):
        self.table = parent
        super(CustomCellComboBoxSelectListStr, self).__init__(self, *args, **kwargs)
    def _get(self):
        value = self.widget._get()
        return value and [value] or []
    def _set(self, arr:list=[""]):
        value = None
        if arr != None and len(arr) > 0:
            [value] = arr
        return self.widget._set(value)
# 单元格-多选下拉框 
class CustomCellComboBoxMultiSelectStr(QWidget):
    def __init__(self, parent,*args, **kwargs):
        super().__init__()
        self.table = parent
        layout = createLayout()
        self.widget = ComboCheckBox()
        layout.addWidget(self.widget)
        self.setLayout(layout)
        self.widget.setFixedHeight(getWidgetHeight())
        self.widget.currentTextChanged.connect(lambda: self._on_currentTextChanged())
    def _get(self):
        return self.widget.Selectlist()
    def _set(self, value:str=None):
        return self.widget.initSelect(value)
    def _init(self, data:dict={}):
        self.data = data

        cacheValue= self.table.get_cache_key_value(self.data.get('name'))
        self._init_select_options(data.get('data') or data.get('onInitData'))
        if cacheValue != None:
            """因此为一旦重新初始化则可能导致数据修改,丢失以缓存的数据,变成原来的样子,因此需要判断cacheValue是否为空初始化数据"""
            self._set(cacheValue)

    def _init_select_options(self, data):
        items = []
        if isinstance(data, list):
            items = data 
        elif isinstance(data, type(lambda x:x)) :
            items = data()
        # self.widget._init(items)
        self.widget.clear()
        self.widget.addItems(items)
    # 当选项发生变化后需要执行的动作
    def _on_currentTextChanged(self):
        # 更新缓存数据
        # 检查切换函数
        onChanged = self.data.get('onChanged')
        if isinstance(onChanged, type(lambda x:x)):
            updateAfterContent = lambda rows:self._update_after_content(rows)
            updateValues = lambda rows:self._update_values(rows)
            onChanged(self.widget._get(), updateAfterContent=updateAfterContent, updateValues=updateValues)
        if hasattr(self.table, 'change_cache_key_value'):
            self.table.change_cache_key_value(self.data.get('name'), self._get())
    # 更新后续内容，一般用于切换时需要动态变化后续的表格行
    def _update_after_content(self, afterRows:list=[]):
        rowIndex = self.data.get('rowIndex', 0)
        # 需要动态更新后续的单元格信息
        self.table._append_rows(rowIndex, afterRows)
    def _update_values(self, values:dict={}):
        # 需要动态更新后续的单元格信息
        if values:
            self.table._update_values(values)
# 验证-字符串列表
class ValidatorStrList(QValidator):
    def __init__(self, parent=None):
        super().__init__(parent)
        
    def validate(self, input_str, pos):
        result = QValidator.Invalid
        self.parent().setStyleSheet("")  # 设置边框颜色为红色
        self.parent().setToolTip("")  # 设置提示信息
        if not input_str:
            return QValidator.Intermediate
        try:
            input_str = input_str.replace("\'", "\"")
            listValue = json.loads(input_str)
            isList = isinstance(listValue, list)
            if isList and all([isinstance(i, str) for i in listValue]):
                result = QValidator.Acceptable
        except:
            pass
        if result == QValidator.Invalid:
            self.parent().setStyleSheet("border: 2px solid red")  # 设置边框颜色为红色
            self.parent().setToolTip("请输入字符串数组,例如:  ['hello','world']")  # 设置提示信息   
        return result 
# # 单元格-字符串列表
# class CustomCellListStr(QWidget):
#     def __init__(self, parent,*args, **kwargs):
#         super(CustomCellListStr, self).__init__(*args, **kwargs)
#         self.table = parent
#         layout = createLayout()
#         self.widget = QLineEdit()
#         self.widget.setPlaceholderText('''字符串数组 ([\'hello\',])''')
#         layout.addWidget(self.widget)
#         self.setLayout(layout)
#         self.widget.setFixedHeight(getWidgetHeight())
#         self.widget.editingFinished.connect(self.on_editing_finished)
#     def on_editing_finished(self):
#         text = self.widget.text()
#         if text and ValidatorStrList(parent=self.widget).validate(text, 0) == QIntValidator.Invalid:
#             QMessageBox.warning(self, 'Error', '请输入字符串数组,例如: [\'hello\',\'world\']!')
#             # self.widget.clear()
#     def _set(self,value):
#         self.widget.setText(str(value))
#     def _get(self):
#         value = self.widget.text()
#         if value != '' :
#             try:
#                 value = value.replace('\'','\"')
#                 listValue = json.loads(value)
#                 if isinstance(listValue, list):
#                     return listValue
#                 # return  value != '' and float(value)
#             except ValueError:
#                 print("输入的不是字符串数组, 字符串数组格式为 ['hello','world']")
#         return None



class CustomCellButton(QWidget):
    def __init__(self, parent, *args, **kwargs):
        super(CustomCellButton, self).__init__(*args, **kwargs)
        self.table = parent
        self.data = {}
        layout = createLayout()
        self.widget = TextChangedButton('''''')
        layout.addWidget(self.widget)
        self.setLayout(layout)
    def _init(self, data:dict={}):
        self.data = data 

    # 注意 value是list 数据
    def _set(self, value:str=''):
        self.widget.setText(value)
    def _get(self):
        value = self.widget.text().strip()
        return value
    
    def _getWidget(self):
        return self.widget


        
# button str cell
class CustomCellListStrButton(QWidget):
    def __init__(self, parent,*args, **kwargs):
        super(CustomCellListStrButton, self).__init__(*args, **kwargs)
        self.table = parent
        self.data = {}
        layout = createLayout()
        self.widget = TextChangedButton(''' 字符串数组 (['hello',])''')
        self.widget.clicked.connect(self.on_click)
        layout.addWidget(self.widget)
        self.setLayout(layout)
        self.widget.textChanged.connect(self.on_editing_finished)
    def _init(self, data:dict={}):
        self.data = data 

    # 注意 value是list 数据
    def _set(self, value:list=[]):
        if  value == None :
            value =[]
        if len(value) == 0:
            self.widget.setText('')
        else:
            self.widget.setText(str(value))

    def _get(self):
        value = self.widget.text().strip()
        if value != '' :
            try:
                value = value.replace('\'','\"')
                listValue = json.loads(value)
                if isinstance(listValue, list):
                    return listValue
                # return  value != '' and float(value)
            except ValueError:
                print("输入的不是字符串数组, 字符串数组格式为 ['hello','world']")
        return []
    def on_editing_finished(self):
        text = self.widget.text()
        if text and ValidatorStrList(parent=self.widget).validate(text, 0) == QIntValidator.Invalid:
            return QMessageBox.warning(self, 'Error', '请输入字符串数组,例如: [\'hello\',\'world\']!')
        name = self.data.get('name')
        if name and hasattr(self.table, 'change_cache_key_value'): 
            self.table.change_cache_key_value(name, self._get())
            # self.widget.clear()
    def on_click(self):
        # 弹出数组对话框设置数据
        list = self._get()
        dlg = DlgDimensionArray('str')
        dlg.onOK.connect(lambda value: self._set(value))
        dlg.init(list)
        dlg.exec_()
# button int cell
class CustomCellListIntButton(QWidget):
    def __init__(self, parent,*args, **kwargs):
        super(CustomCellListIntButton, self).__init__(*args, **kwargs)
        self.table = parent
        self.data = {}
        layout = createLayout()
        self.widget = TextChangedButton('''整数数组 ([1,])''')
        self.widget.clicked.connect(self.on_click)
        layout.addWidget(self.widget)
        self.setLayout(layout)
        self.widget.textChanged.connect(self.on_editing_finished)
    
    def _init(self, data:dict={}):
        self.data = data 
    # 注意 value是list 数据
    def _set(self, value:list=[]):
        if value == None:
            value = []

        if len(value) == 0:
            self.widget.setText('')
        else:
            self.widget.setText(str(value))
    def _get(self):
        value = self.widget.text().strip()
        if value != '' :
            try:
                value = value.replace('\'','\"')
                listValue = json.loads(value)
                if isinstance(listValue, list):
                    return listValue
                # return  value != '' and float(value)
            except ValueError:
                print("输入的不是整数数组, 整数数组格式为 [1, 2]")
        return []
    def on_editing_finished(self):
        text = self.widget.text()
        if text and ValidatorIntList(parent=self.widget).validate(text, 0) == QIntValidator.Invalid:
            return QMessageBox.warning(self, 'Error', '请输入整数数组,例如: [1, 2]!')
        name = self.data.get('name')
        if name and hasattr(self.table, 'change_cache_key_value'): 
            self.table.change_cache_key_value(name, self._get())
            # self.widget.clear()
    def on_click(self):
        # 弹出数组对话框设置数据
        list = self._get()
        dlg = DlgDimensionArray('int')
        dlg.onOK.connect(lambda value: self._set(value))
        dlg.init(list)
        dlg.exec_()
# button int cell
class CustomCellListFloatButton(QWidget):
    def __init__(self, parent,*args, **kwargs):
        super(CustomCellListFloatButton, self).__init__(*args, **kwargs)
        self.table = parent
        self.data = {}
        layout = createLayout()
        self.widget = TextChangedButton('''浮点数组 ([1.00,])''')
        self.widget.clicked.connect(self.on_click)
        layout.addWidget(self.widget)
        self.setLayout(layout)
        self.widget.textChanged.connect(self.on_editing_finished)
    
    def _init(self, data:dict={}):
        self.data = data 
    # 注意 value是list 数据
    def _set(self, value:list=[]):
        if value == None:
            value = []
        if len(value) == 0:
            self.widget.setText('')
        else:
            self.widget.setText(str(value))
    def _get(self):
        value = self.widget.text().strip()
        if value != '' :
            try:
                value = value.replace('\'','\"')
                listValue = json.loads(value)
                if isinstance(listValue, list):
                    return listValue
                # return  value != '' and float(value)
            except ValueError:
                print("输入的不是浮点数组, 浮点数组格式为 [1.00,]")
        return []
    def on_editing_finished(self):
        text = self.widget.text()
        if text and ValidatorFloatList(parent=self.widget).validate(text, 0) == QIntValidator.Invalid:
            return QMessageBox.warning(self, 'Error', '请输入浮点数组,例如: [1.00,]!')
            # self.widget.clear()
        name = self.data.get('name')
        if name and hasattr(self.table, 'change_cache_key_value'): 
            self.table.change_cache_key_value(name, self._get())
    def on_click(self):
        # 弹出数组对话框设置数据
        list = self._get()
        dlg = DlgDimensionArray('float')
        dlg.onOK.connect(lambda value: self._set(value))
        dlg.init(list)
        dlg.exec_()
class CustomCellListListFloatButton(QWidget):
    def __init__(self, parent,*args, **kwargs):
        super(CustomCellListListFloatButton, self).__init__(*args, **kwargs)
        self.table = parent
        self.data = {}
        layout = createLayout()
        self.widget = TextChangedButton('''二维浮点数组 ([[1.00,],])''')
        self.widget.clicked.connect(self.on_click)
        layout.addWidget(self.widget)
        self.setLayout(layout)
        self.widget.textChanged.connect(self.on_editing_finished)
    def _init(self, data:dict={}):
        self.data = data 
    def on_editing_finished(self):
        text = self.widget.text()
        if text and ValidatorFloatNdim2List(parent=self.widget).validate(text, 0) == QIntValidator.Invalid:
            return QMessageBox.warning(self, 'Error', '请输入二维浮点数组,例如: [[1.0]] !')
        name = self.data.get('name')
        
        if name and hasattr(self.table, 'change_cache_key_value'): 
            self.table.change_cache_key_value(name, self._get())
            # self.widget.clear()
    def on_click(self):
        # 弹出数组对话框设置数据
        list = self._get()
        dlg = DlgDimensionDoubleArray('List[List[float]]')
        dlg.onOK.connect(lambda value: self._set(value))
        dlg.init(list)
        dlg.exec_()
    # 注意 value是list 数据
    def _set(self, value:list=[]):
        if value == None:
            value = []
        if len(value) == 0:
            self.widget.setText('')
        else:
            self.widget.setText(str(value))
    def _get(self):
        value = self.widget.text().strip()
        if value != '' :
            try:
                listValue = json.loads(value)
                if isinstance(listValue, list):
                    return listValue
            except ValueError:
                print("输入的不是二维浮点数数组, 数组格式为 [[1.0,2.0],[2.0],...]")
        return []
class CustomCellListListIntButton(QWidget):
    def __init__(self, parent,*args, **kwargs):
        super(CustomCellListListIntButton, self).__init__(*args, **kwargs)
        self.table = parent
        self.data = {}
        layout = createLayout()
        self.widget = TextChangedButton('''二维整数数组 ([[1,],])''')
        self.widget.clicked.connect(self.on_click)
        layout.addWidget(self.widget)
        self.setLayout(layout)
        
        self.widget.textChanged.connect(self.on_editing_finished)
    def on_editing_finished(self):
        text = self.widget.text()
        if text and ValidatorIntNdim2List(parent=self.widget).validate(text, 0) == QIntValidator.Invalid:
            return QMessageBox.warning(self, 'Error', '请输入二维浮点数组,例如: [[1.0]] !')
        name = self.data.get('name')
        
        if name and hasattr(self.table, 'change_cache_key_value'): 
            self.table.change_cache_key_value(name, self._get())
            # self.widget.clear()
    def on_click(self):
        # 弹出数组对话框设置数据
        list = self._get()
        dlg = DlgDimensionDoubleArray('List[List[int]]')
        dlg.onOK.connect(lambda value: self._set(value))
        dlg.init(list)
        dlg.exec_()
    def _init(self, data:dict={}):
        self.data = data 
    # 注意 value是list 数据
    def _set(self, value:list=[]):
        if value == None:
            value = []
        if len(value) == 0:
            self.widget.setText('')
        else:
            self.widget.setText(str(value))
    def _get(self):
        value = self.widget.text().strip()
        if value != '' :
            try:
                listValue = json.loads(value)
                if isinstance(listValue, list):
                    return listValue
            except ValueError:
                print("输入的不是二维数组, 数组格式为 [[1,],...]")
        return []
class CustomCellListListStrButton(QWidget):
    def __init__(self, parent,*args, **kwargs):
        super(CustomCellListListStrButton, self).__init__(*args, **kwargs)
        self.table = parent
        self.data = {}
        layout = createLayout()
        self.widget = TextChangedButton('''二维数组 ([["hello",],])''')
        self.widget.clicked.connect(self.on_click)
        layout.addWidget(self.widget)
        self.setLayout(layout)
        self.widget.textChanged.connect(self.on_editing_finished)
    def on_editing_finished(self):
        text = self.widget.text()
        if text and ValidatorIntNdim2List(parent=self.widget).validate(text, 0) == QIntValidator.Invalid:
            return QMessageBox.warning(self, 'Error', '二维数组,例如: [["hello",],] !')
        name = self.data.get('name')
        
        if name and hasattr(self.table, 'change_cache_key_value'): 
            self.table.change_cache_key_value(name, self._get())
    def on_click(self):
        # 弹出数组对话框设置数据
        list = self._get()
        dlg = DlgDimensionDoubleArray('List[List[str]]')
        dlg.onOK.connect(lambda value: self._set(value))
        dlg.init(list)
        dlg.exec_()
    def _init(self, data:dict={}):
        self.data = data
    # 注意 value是list 数据
    def _set(self, value:list=[]):
        if value == None:
            value = []
        if len(value) == 0:
            self.widget.setText('')
        else:
            self.widget.setText(str(value))
    def _get(self):
        value = self.widget.text().strip()
        if value != '' :
            try:
                listValue = json.loads(value)
                if isinstance(listValue, list):
                    return listValue
            except ValueError:
                print('输入的不是二维数组, 数组格式为 [["hello",...],...]')
        return []
# 数组追加按钮
class CustomCellAddButton(QWidget):
    def __init__(self, on_click, *args, **kwargs):
        super(CustomCellAddButton, self).__init__(*args, **kwargs)
        layout = createLayout()
        self.widget = QPushButton('添加行')
        self.widget.clicked.connect(on_click)
        layout.addWidget(self.widget)
        self.setLayout(layout)
        self.widget.setFixedHeight(getWidgetHeight())
        self.widget.setStyleSheet("""
            QPushButton {
                width:100%;
                height:100%;
                margin:0;
            }
        """)
# 验证-浮点数列表
class ValidatorFloatList(QValidator):
    def __init__(self, parent=None):
        super().__init__(parent)
    def validate(self, input_str, pos):
        result = QValidator.Invalid
        self.parent().setStyleSheet("")  # 设置边框颜色为红色
        self.parent().setToolTip("")  # 设置提示信息
        if not input_str:
            return QValidator.Intermediate
        try:
            listValue = json.loads(input_str)
            isList = isinstance(listValue, list)
            if isList and all([isinstance(i, float) for i in listValue]):
                result = QValidator.Acceptable
        except:
            pass
        if result == QValidator.Invalid:
            self.parent().setStyleSheet("border: 2px solid red")  # 设置边框颜色为红色
            self.parent().setToolTip("请输入浮点数组,例如: [1.1,2.0]")  # 设置提示信息   
        return result 
# 单元格-浮点数列表
# class CustomCellListFloat(QWidget):
#     def __init__(self, parent,*args, **kwargs):
#         super(CustomCellListFloat, self).__init__(*args, **kwargs)
#         self.table = parent
#         self.data = {}
#         layout = createLayout()
#         self.widget = QLineEdit()
#         self.widget.setPlaceholderText('''浮点数数组 ([1.0])''')
#         layout.addWidget(self.widget)
#         self.setLayout(layout)
#         self.widget.setFixedHeight(getWidgetHeight())
#         self.widget.editingFinished.connect(self.on_editing_finished)
#     def on_editing_finished(self):
#         text = self.widget.text()
#         if text and ValidatorFloatList(parent=self.widget).validate(text, 0) == QIntValidator.Invalid:
#             QMessageBox.warning(self, 'Error', '请输入浮点数数组,例如: [1.0] !')
#             # self.widget.clear()
    
#     def _set(self,value):
#         self.widget.setText(str(value))
#     def _get(self):
#         value = self.widget.text()
#         if value != '' :
#             try:
#                 listValue = json.loads(value)
#                 if isinstance(listValue, list):
#                     return listValue
#             except ValueError:
#                 print("输入的不是浮点数数组, 数组格式为 [1.0,2.0,...]")
#         return None
        # if value != '' :
        #     value = value.replace('\'','\"')
        #     return json.loads(value)
        # return None
# 验证-二维浮点数数组
class ValidatorFloatNdim2List(QValidator):
    def __init__(self, parent=None):
        super().__init__(parent)
        
    def validate(self, input_str, pos):
        result = QValidator.Invalid
        self.parent().setStyleSheet("")  # 设置边框颜色为红色
        self.parent().setToolTip("")  # 设置提示信息
        if not input_str:
            return QValidator.Intermediate
        try:
            listValue = json.loads(input_str)
            isList = isinstance(listValue, list)
            if isList and all([isinstance(i, list) for i in listValue]):
                isError = False
                # 循环遍历 判断数组中每个项是否为 浮点数组
                for floatArr in listValue:
                    if not isinstance(floatArr, list) or not all([isinstance(i, float) for i in floatArr]):
                        isError = True
                if not isError:
                    result = QValidator.Acceptable
        except:
            pass
        if result == QValidator.Invalid:
            self.parent().setStyleSheet("border: 2px solid red")  # 设置边框颜色为红色
            self.parent().setToolTip("请输入二维浮点数组,例如: [[0.1],[1.2]]")  # 设置提示信息
        return result
# 单元格-二维浮点数数组
# class CustomCellListListFloat(QWidget):
#     def __init__(self, parent,*args, **kwargs):
#         super(CustomCellListListFloat, self).__init__(*args, **kwargs)
#         self.table = parent
#         layout = createLayout()
#         self.widget = QLineEdit()
#         self.widget.setPlaceholderText('''二维浮点数数组 ([[1.0]])''')
#         # self.widget.setValidator(ValidatorFloatNdim2List(parent=self.widget))

#         layout.addWidget(self.widget)
#         self.setLayout(layout)
#         self.widget.setFixedHeight(getWidgetHeight())
#         self.widget.editingFinished.connect(self.on_editing_finished)
#     def on_editing_finished(self):
#         text = self.widget.text()
#         if text and ValidatorFloatNdim2List(parent=self.widget).validate(text, 0) == QIntValidator.Invalid:
#             QMessageBox.warning(self, 'Error', '请输入二维浮点数组,例如: [[1.0]] !')
#             # self.widget.clear()
#     def _set(self,value):
#         self.widget.setText(str(value))
#     def _get(self):
#         value = self.widget.text()
#         if value != '' :
#             try:
#                 listValue = json.loads(value)
#                 if isinstance(listValue, list):
#                     return listValue
#             except ValueError:
#                 print("输入的不是二维浮点数数组, 数组格式为 [[1.0,2.0],[2.0],...]")
#         return None

# 验证-二维整数数组
class ValidatorIntNdim2List(QValidator):
    def __init__(self, parent=None):
        super().__init__(parent)
        
    def validate(self, input_str, pos):
        result = QValidator.Invalid
        self.parent().setStyleSheet("")  # 设置边框颜色为红色
        self.parent().setToolTip("")  # 设置提示信息
        if not input_str:
            return QValidator.Intermediate
        try:
            listValue = json.loads(input_str)
            isList = isinstance(listValue, list)
            if isList and all([isinstance(i, list) for i in listValue]):
                isError = False
                # 循环遍历 判断数组中每个项是否为 浮点数组
                for intArr in listValue:
                    if not isinstance(intArr, list) or not all([isinstance(i, int) for i in intArr]):
                        isError = True
                if not isError:
                    result = QValidator.Acceptable
        except:
            pass
        if result == QValidator.Invalid:
            self.parent().setStyleSheet("border: 2px solid red")  # 设置边框颜色为红色
            self.parent().setToolTip("请输入二维整数数组,例如: [[0],[1]]")  # 设置提示信息   
        return result 
# 单元格-二维整数数组
# class CustomCellListListInt(QWidget):
#     def __init__(self, parent,*args, **kwargs):
#         super(CustomCellListListInt, self).__init__(*args, **kwargs)
#         self.table = parent
#         layout = createLayout()
#         self.widget = QLineEdit()
#         self.widget.setPlaceholderText('''二维整数数组 ([[1]])''')
#         # regex =  QtCore.QRegExp(r'^(\d+\s*,\s*)+\d+$')
#         # self.widget.setValidator(ValidatorIntNdim2List(parent=self.widget))
#         layout.addWidget(self.widget)
#         self.setLayout(layout)
#         self.widget.setFixedHeight(getWidgetHeight())
#         self.widget.editingFinished.connect(self.on_editing_finished)
#     def on_editing_finished(self):
#         text = self.widget.text()
#         if text and ValidatorIntNdim2List(parent=self.widget).validate(text, 0) == QIntValidator.Invalid:
#             QMessageBox.warning(self, 'Error', '请输入二维整数数组,例如: [[1],[0]] !')
#             # self.widget.clear()
#     def _set(self,value):
#         self.widget.setText(str(value))
#     def _get(self):
#         value = self.widget.text()
#         if value != '' :
#             try:
#                 listValue = json.loads(value)
#                 if isinstance(listValue, list):
#                     return listValue
#             except ValueError:
#                 print("输入的不是二维整数数组, 数组格式为 [[1]]")
#         return None
#         # if value != '' :
#         #     value = value.replace('\'','\"')
#         #     return json.loads(value)
#         # return None
#         #    
# 验证-整数数组
class ValidatorIntList(QValidator):
    def __init__(self, parent=None):
        super().__init__(parent)
        
    def validate(self, input_str, pos):
        result = QValidator.Invalid
        self.parent().setStyleSheet("")  # 设置边框颜色为红色
        self.parent().setToolTip("")  # 设置提示信息
        if not input_str:
            return QValidator.Intermediate
        try:
            listValue = json.loads(input_str)
            isList = isinstance(listValue, list)
            if isList and all([isinstance(i, int) for i in listValue]):
                result = QValidator.Acceptable
        except:
            pass
        if result == QValidator.Invalid:
            self.parent().setStyleSheet("border: 2px solid red")  # 设置边框颜色为红色
            self.parent().setToolTip("请输入整数数组,例如:  [0,1,2]")  # 设置提示信息   
        return result 
# 单元格-整数数组
# class CustomCellListInt(QWidget):
#     def __init__(self, parent,*args, **kwargs):
#         super(CustomCellListInt, self).__init__(*args, **kwargs)
#         self.table = parent
#         layout = createLayout()
#         self.widget = QLineEdit()
#         self.widget.setPlaceholderText('''整数数组 ([1,])''')
#         layout.addWidget(self.widget)
#         self.setLayout(layout)
#         self.widget.setFixedHeight(getWidgetHeight())
#         self.widget.editingFinished.connect(self.on_editing_finished)
#     def on_editing_finished(self):
#         text = self.widget.text()
#         if text and ValidatorIntList(parent=self.widget).validate(text, 0) == QIntValidator.Invalid:
#             QMessageBox.warning(self, 'Error', '请输入整数数组,例如: [0,1,2] !')
#             # self.widget.clear()

#     def _set(self,value):
#         self.widget.setText(str(value))
#     def _get(self):
#         value = self.widget.text()
#         if value != '' :
#             try:
#                 listValue = json.loads(value)
#                 if isinstance(listValue, list):
#                     return listValue
#             except ValueError:
#                 print("输入的不是二维整数数数组, 数组格式为 [1,2,...]")
#         return None
#         # if value != '' :
#         #     value = value.replace('\'','\"')
#         #     return json.loads(value)
#         # return None
# 单元格-参数列
class CustomCellLabel(QWidget):
    def __init__(self, parent,*args, **kwargs):
        super(CustomCellLabel, self).__init__(*args, **kwargs)
        self.table = parent
        layout = createLayout()
        # layout = QHBoxLayout()
        # layout.setSpacing(0)
        # layout.setContentsMargins(0,0,0,0)
        # 设置背景色
        self.widget = QLabel()
        layout.addWidget(self.widget)
        # self.setStyleSheet("background-color: #f0f0f0 ")
        self.setLayout(layout)
        self.widget.setFixedHeight(getWidgetHeight())
        # self.widget.setToolTipDuration(1000)
        # self.widget.installEventFilter(self)
        # self._toolTip = ToolTip(parent=self)

    def _set(self, value):
        self.widget.setText(str(value))
        # self.widget.setToolTip(str(value))
    def _get(self):
        return self.widget.text()

    # def eventFilter(self, obj, e):
    #     if not self._toolTip:
    #         return super().eventFilter(obj, e)
    #     if obj is self:
    #         return super().eventFilter(obj, e)

    #     tip = self._toolTip
    #     if e.type() == QtCore.QEvent.Enter:
    #         tip.setText(obj.toolTip())
    #         tip.setDuration(obj.toolTipDuration())
    #         pos = obj.mapTo(self, QtCore.QPoint(0, 0))
    #         x = pos.x() + obj.width()//2 - tip.width()//2
    #         y = pos.y() - 5 - tip.height()
    #         # adjust postion to prevent tooltips from appearing outside the window
    #         x = min(max(5, x), self.width() - tip.width() - 5)
    #         y = min(max(5, y), self.height() - tip.height() - 5)
    #         tip.move(x, y)
    #         tip.show()
    #     elif e.type() == QtCore.QEvent.Leave:
    #         tip.hide()
    #     elif e.type() == QtCore.QEvent.ToolTip:
    #         return True
    #     return super().eventFilter(obj, e)
    #获得鼠标进入的单元格对应的QTableWidgetItem对象
class CustomCellGroupLine(QWidget):
    def __init__(self, parent,*args, **kwargs):
        super(CustomCellGroupLine, self).__init__(*args, **kwargs)
        # self.setBackground(QColor(240, 240, 240))
        self.table = parent
        layout = createLayout()
        self.widget = QLabel()
        layout.addWidget(self.widget)
        self.setLayout(layout)
        self.widget.setFixedHeight(getWidgetHeight())
        self.widget.setToolTipDuration(1000)
        self.widget.installEventFilter(self)

    def _set(self, value):
        pass
        # self.widget.setToolTip(str(value))
    def _get(self):
        return ''
    
class CustomLabelInt(QWidget):
    def __init__(self, parent,*args, **kwargs):
        super(CustomLabelInt, self).__init__(*args, **kwargs)
        self.table = parent
        layout = createLayout()
        self.widget = QLabel()
        layout.addWidget(self.widget)
        self.setLayout(layout)
        self.widget.setFixedHeight(getWidgetHeight())
    def _set(self, value):
        self.widget.setText(str(value))
    def _get(self):
        val = self.widget.text()
        if val != '':
            return int(val)
        return None
# 定义行自增长数组表格
class CustomArrayTableWidget(QTableWidget):
    TYPES = {
        'text': {
            'widget':CustomCellStr,
        },
        'label': {
            'widget':CustomCellLabel,
        },
        'str': {
            'widget':CustomCellStr,
            'placeholder':'字符串'
        },
        'int': {
            'widget':CustomCellInt,
            'placeholder':'整数'
        },
        'float': {
            'widget':CustomCellFloat,
            'placeholder':'浮点数'
        },
    }
    def __init__(self, cellType, *args, **kwargs):
        super(CustomArrayTableWidget, self).__init__(*args, **kwargs)
        # self.cellChanged.connect(self.handleCellChanged)
        # 标题头
        self.cellType = cellType or 'text'
        self._initHorHeaders(['数组赋值'])
        self._initVerticalHeaders()
        self.setEditTriggers(QAbstractItemView.NoEditTriggers)
        self.setSelectionMode(QAbstractItemView.NoSelection)
    def _initHorHeaders(self, titles):
        _translate = QtCore.QCoreApplication.translate
        self.setColumnCount(len(titles))
        # 水平分割宽度
        self.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
        # 不高亮标题
        self.horizontalHeader().setHighlightSections(False)
        for i in range(len(titles)):
            title =_translate("UiCustomTable", titles[i])  
            widgetItem = QTableWidgetItem(title)
            font = widgetItem.font()
            font.setBold(True)
            widgetItem.setFont(font)
            self.setHorizontalHeaderItem(i, widgetItem)
    def _initVerticalHeaders(self):
        # 垂直分割宽度
        self.verticalHeader().setSectionResizeMode(QHeaderView.Fixed)
        # 不高亮标题
        self.verticalHeader().setVisible(True)
    # 初始化显示的数据  注意 数据必须转成数组
    def _init(self, newData:list=[]):
        rowCount = len(newData) 
        self.setRowCount(rowCount+1)
        # 字典list 按字典中的title 属性排序 
        if rowCount > 0:
            for row in range(rowCount):
                value = newData[row]
                # 属性列 label 显示
                self.setRowHeight(row, 40)
                newValue = ''
                if value != None:
                    newValue = value
                widgetCell = self.__initCellWidget(self.cellType, newValue)
                self.setCellWidget(row, 0, widgetCell)
        # 增加一个+号按钮
        self.setCellWidget(rowCount, 0, self.__initAddBtnCellWidget())
    def _get(self):
        count = self.rowCount() -1 # 最后一行始终为添加按钮
        data = []
        for row in range(count):
            value = self.cellWidget(row,0)._get()
            if value != None:
                data.append(value)
        return data 
    def __initCellWidget(self, type, value):
        cellTypeItem = (type and self.TYPES.get(type) or self.TYPES.get('text'))
        cell = cellTypeItem.get('widget')(self)
        cell.widget.setPlaceholderText(cellTypeItem.get('placeholder',''))
        cell._set(value)
        return cell
    def __initAddBtnCellWidget(self):
        return CustomCellAddButton(lambda *args: self.addRowHandle())
    def addRowHandle(self):
        rowCount = self.rowCount()-1
        self.insertRow(rowCount)
        widgetCell = self.__initCellWidget(self.cellType, '')
        self.setRowHeight(rowCount, 40)
        self.setCellWidget(rowCount, 0, widgetCell)
        self.scrollToBottom()
        # 设置最新行的widget 的焦点
        widgetCell.widget.setFocus()

class CustomDoubleArrayTableWidget(QTableWidget):
    TYPES = {
        'List[List[str]]': CustomCellListStrButton,
        'List[List[int]]': CustomCellListIntButton,
        'List[List[float]]': CustomCellListFloatButton,
    }
    def __init__(self, cellType, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # self.cellChanged.connect(self.handleCellChanged)
        # 标题头
        self.cellType = cellType or 'List[List[str]]'
        self._initHorHeaders(['数组'])
        self._initVerticalHeaders()
        self.setEditTriggers(QAbstractItemView.NoEditTriggers)
        self.setSelectionMode(QAbstractItemView.NoSelection)
    def _initHorHeaders(self, titles):
        _translate = QtCore.QCoreApplication.translate
        self.setColumnCount(len(titles))
        # 水平分割宽度
        self.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
        # 不高亮标题
        self.horizontalHeader().setHighlightSections(False)
        for i in range(len(titles)):
            title =_translate("UiCustomTable", titles[i])  
            widgetItem = QTableWidgetItem(title)
            font = widgetItem.font()
            font.setBold(True)
            widgetItem.setFont(font)
            self.setHorizontalHeaderItem(i, widgetItem)
    def _initVerticalHeaders(self):
        # 垂直分割宽度
        self.verticalHeader().setSectionResizeMode(QHeaderView.Fixed)
        # 不高亮标题
        self.verticalHeader().setVisible(True)
    # 初始化显示的数据  注意 数据必须转成数组
    def _init(self, newData:list=[]):
        rowCount = len(newData) 
        self.setRowCount(rowCount+1)
        # 字典list 按字典中的title 属性排序 
        if rowCount > 0:
            for row in range(rowCount):
                value = newData[row]
                # 属性列 label 显示
                self.setRowHeight(row, 40)
                newValue = ''
                if value != None:
                    newValue = value
                widgetCell = self.__initCellWidget(self.cellType, newValue)
                self.setCellWidget(row, 0, widgetCell)
        # 增加一个+号按钮
        self.setCellWidget(rowCount, 0, self.__initAddBtnCellWidget())
    def _get(self):
        count = self.rowCount() -1 # 最后一行始终为添加按钮
        data = []
        for row in range(count):
            value = self.cellWidget(row,0)._get()
            if value != None:
                data.append(value)
        return data 
    def __initCellWidget(self, type, value):
        cellTypeItem = (type and self.TYPES.get(type) or self.TYPES.get('text'))
        cell = cellTypeItem(self)
        cell._set(value)
        return cell
    def __initAddBtnCellWidget(self):
        return CustomCellAddButton(lambda *args: self.addRowHandle())
    def addRowHandle(self):
        rowCount = self.rowCount()-1
        self.insertRow(rowCount)
        widgetCell = self.__initCellWidget(self.cellType, '')
        self.setRowHeight(rowCount, 40)
        self.setCellWidget(rowCount, 0, widgetCell)
        self.scrollToBottom()
# 一维数组ui
class UiArrayTable(object):
    def __init__(self, cellType:str='str'):
        self.cellType = cellType
    def setupUi(self, parent):
        parent.setObjectName("DlgDimensionArray")
        parent.resize(600, 400)
        self.parent = parent
        row = 0
        mainLayout = QtWidgets.QGridLayout(parent)
        mainLayout.setContentsMargins(11, 11, 11, 11)
        mainLayout.setSpacing(6)
        mainLayout.setObjectName("gridLayout")
        # 数据行
        self.tableModel = CustomArrayTableWidget(self.cellType)
        self.tableModel.setObjectName('tableModel')
        # 不显示标题
        self.tableModel.setMinimumHeight(0)
        # self.tableModel.horizontalHeader().setVisible(False)
        mainLayout.addWidget(self.tableModel, row, 0, 1, 1)
        row += 1
        # 底部按钮行
        layoutBtns = QtWidgets.QHBoxLayout()
        layoutBtns.setSpacing(6)
        layoutBtns.addStretch(1)
        self.pbtnOK = QtWidgets.QPushButton()
        self.pbtnOK.setObjectName("pbtnOK")
        self.pbtnOK.setMinimumSize(QtCore.QSize(0, 30))
        layoutBtns.addWidget(self.pbtnOK)
        self.pbtnCacnel = QtWidgets.QPushButton()
        self.pbtnCacnel.setObjectName("pbtnCacnel")
        self.pbtnCacnel.setMinimumSize(QtCore.QSize(0, 30))
        layoutBtns.addWidget(self.pbtnCacnel)
        layoutBtns.addStretch(1)
        mainLayout.addLayout(layoutBtns, row, 0, 1, 1)
        self.retranslateUi()
        QtCore.QMetaObject.connectSlotsByName(parent)
        self.pbtnCacnel.clicked.connect(parent.reject)
    def retranslateUi(self):
        _translate = QtCore.QCoreApplication.translate
        self.parent.setWindowTitle(_translate("DlgPydAttributes", "修改数组"))
        self.pbtnOK.setText(_translate("pbtnOK", "保存"))
        self.pbtnCacnel.setText(_translate("pbtnCacnel", "关闭"))
    # 初始化数据
    def set(self, data:list=[]) -> None:
        self.tableModel._init(data)
    def get(self):
        return self.tableModel._get()
# 二维数组ui 表格
class UiDoubleArrayTable(object):
    def __init__(self, cellType:str='List[List[str]]'):
        self.cellType = cellType
    def setupUi(self, parent):
        parent.setObjectName("DlgDimensionArray")
        parent.resize(600, 400)
        self.parent = parent
        row = 0
        mainLayout = QtWidgets.QGridLayout(parent)
        mainLayout.setContentsMargins(11, 11, 11, 11)
        mainLayout.setSpacing(6)
        mainLayout.setObjectName("gridLayout")
        # 数据行
        self.tableModel = CustomDoubleArrayTableWidget(self.cellType)
        self.tableModel.setObjectName('tableModel')
        # 不显示标题
        self.tableModel.setMinimumHeight(0)
        # self.tableModel.horizontalHeader().setVisible(False)
        mainLayout.addWidget(self.tableModel, row, 0, 1, 1)
        row += 1
        # 底部按钮行
        layoutBtns = QtWidgets.QHBoxLayout()
        layoutBtns.setSpacing(6)

        layoutBtns.addStretch(1)
        self.pbtnOK = QtWidgets.QPushButton()
        self.pbtnOK.setObjectName("pbtnOK")
        self.pbtnOK.setMinimumSize(QtCore.QSize(0, 30))
        layoutBtns.addWidget(self.pbtnOK)
        self.pbtnCacnel = QtWidgets.QPushButton()
        self.pbtnCacnel.setObjectName("pbtnCacnel")
        self.pbtnCacnel.setMinimumSize(QtCore.QSize(0, 30))
        layoutBtns.addWidget(self.pbtnCacnel)
        layoutBtns.addStretch(1)
        mainLayout.addLayout(layoutBtns, row, 0, 1, 1)
        self.retranslateUi()
        QtCore.QMetaObject.connectSlotsByName(parent)
        self.pbtnCacnel.clicked.connect(parent.reject)
    def retranslateUi(self):
        _translate = QtCore.QCoreApplication.translate
        self.parent.setWindowTitle(_translate("DlgCustomAttributes", "修改数组"))
        self.pbtnOK.setText(_translate("pbtnOK", "保存"))
        self.pbtnCacnel.setText(_translate("pbtnCacnel", "关闭"))
    # 初始化数据
    def set(self, data:list=[]) -> None:
        self.tableModel._init(data)
    def get(self):
        return self.tableModel._get()
# 弹出一维数组对话框
class DlgDimensionArray(QtWidgets.QDialog):
    onOK = QtCore.pyqtSignal(list)
    def __init__(self, cellType:str='str'):
        super().__init__()  # 调用父类构造函数
        self.ui = UiArrayTable(cellType)  # 创建__ui对象
        self.ui.setupUi(self)  # 构造__ui
        self.finished.connect(self.closeDialog)
    def closeDialog(self):
        self.onOK.disconnect()
        pass
    @QtCore.pyqtSlot()
    def on_pbtnOK_clicked(self):
        data = self.ui.get()
        # 判断触发 ok 按键的回调函数是否存在 且为 函数
        self.onOK.emit(data)
        self.accept()
    def init(self, data:list={}) -> QtWidgets.QDialog:
        self.ui.set(data)
        return self
# 弹出二维数组对话框
class DlgDimensionDoubleArray(QtWidgets.QDialog):
    onOK = QtCore.pyqtSignal(list)
    def __init__(self, cellType:str='List[List[str]]'):
        super().__init__()  # 调用父类构造函数
        self.ui = UiDoubleArrayTable(cellType)  # 创建__ui对象
        self.ui.setupUi(self)  # 构造__ui
        self.finished.connect(self.closeDialog)
    def closeDialog(self):
        self.onOK.disconnect()
        pass
    @QtCore.pyqtSlot()
    def on_pbtnOK_clicked(self):
        data = self.ui.get()
        # 判断触发 ok 按键的回调函数是否存在 且为 函数
        self.onOK.emit(data)
        self.accept()
    def init(self, data:list={}) -> QtWidgets.QDialog:
        self.ui.set(data)
        return self
# 表格
class UiCustomTable(QTableWidget):
    TYPES = {
        'text': CustomCellStr,
        'path': CustomCellPath,
        'vtkGroup': CustomCellVtkGroup,
        'file': CustomCellFile,
        '.inp': CustomCellInpFile,
        '.bdf': CustomCellBdfFile,
        '.sdb': CustomCellSdbFile,
        '.vtk': CustomCellVtkFile,
        '.vtu': CustomCellVtuFile,
        # '.dsp': CustomCellDspFile,
        '.vtr':CustomCellVtrFile,
        '.bat':CustomCellBatFile,
        # 'groupLine':CustomCellGroupLine,

        # 'NONE': CustomCellLabel, # 仅作为分组结构

        'label': CustomCellLabel,
        'labelInt':CustomLabelInt,
        'str': CustomCellStr,
        'int':CustomCellInt,
        'float':CustomCellFloat,
        'bool': CustomCellBool,
        'btn':CustomCellButton,
        'checkbox': CustomCellCheckbox,
        #  list 改为按钮， 点击弹出对话框不允许直接修改 因此上面部分内容可以忽略
        'List[str]': CustomCellListStrButton,
        'List[int]': CustomCellListIntButton,
        'List[float]': CustomCellListFloatButton,
        # 二维数组
        'List[List[float]]': CustomCellListListFloatButton,
        'List[List[int]]': CustomCellListListIntButton,
        'List[List[str]]': CustomCellListListStrButton,

        # 选择 单选
        #  选择一个，值非数组
        #  选择一个，值是数组
        'select[str]': CustomCellComboBoxSelectStr,
        'select[int]': CustomCellComboBoxSelectInt,
        'select[float]': CustomCellComboBoxSelectFloat,
        'select[List[str]]': CustomCellComboBoxSelectListStr,
        'multiSelect[str]': CustomCellComboBoxMultiSelectStr,
        #  3. 选择多个，值是数组 暂不考虑
        #  4. 选择多个，值非数组 暂不考虑
    }
    def __init__(self, *args, parent=None, **kwargs):
        super(UiCustomTable, self).__init__(*args, **kwargs)
        self.parent = parent
        self.__cacheData = {} # 记录缓存数据信息
        self._isInit = False # 是否初始化 因为需要组件值变化会修改缓存数据，因此需要在初始化时不更新缓存数据
        # 标题头
        self._initHorHeaders(['属性', '值'])
        
        self._initVerticalHeaders()
        self.setEditTriggers(QAbstractItemView.NoEditTriggers)
        # 下面两行设置第一列宽度 
        self.horizontalHeader().setSectionResizeMode(0, QHeaderView.Interactive)
        self.setColumnWidth(0,200)
        self.setSelectionMode(QAbstractItemView.NoSelection)

        # 安装事件过滤器
        self.installEventFilter(self)
        self.setMouseTracking(True)
        


    def _initHorHeaders(self, titles):
        _translate = QtCore.QCoreApplication.translate
        self.setColumnCount(len(titles))
        # 水平分割宽度
        self.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
        # 不高亮标题
        self.horizontalHeader().setHighlightSections(False)
        for i in range(len(titles)):
            title =_translate("UiCustomTable", titles[i])  
            widgetItem = QTableWidgetItem(title)
            font = widgetItem.font()
            font.setBold(True)
            widgetItem.setFont(font)
            self.setHorizontalHeaderItem(i, widgetItem)


    def _initVerticalHeaders(self):
        # 垂直分割宽度
        self.verticalHeader().setSectionResizeMode(QHeaderView.Fixed)
        # 不高亮标题
        self.verticalHeader().setVisible(False)
    # 初始化显示的数据  注意 数据必须转成数组
    def _init(self, rows:list=[], data:dict={}):
        self._isInit = True
        self.__cacheData = data
        self._clear()
        self.setRowCount(0)
        self._init_rows(0, rows)
        self._isInit = False


    # 在指定row 后面增加数据行
    def _append_rows(self, row, data):
        # 移除所有的行
        self._remove_rows_after(row)
        self._init_rows(row+1, data)
    # 初始化数据行
    def _init_rows(self, rowIndex:int=0, data:list=[]):
        # if sortBy == 'title':
        #     newData = sorted(data, key=lambda x:x.get('sort') or x.get('title') or x.get('name'))
        # 存在动态更新的行
        for row in range(len(data)):
            count = self.rowCount()
            # 适用于动态更新行
            if (rowIndex+1) <= count:
                rowIndex = count
            self.setRowCount(rowIndex+1)
            self.setRowHeight(rowIndex, 40)
            item = data[row]
            # 行标题
            titleWidget = TableWidgetItemRowHeader(item, groupLine=item.get('groupLine', False))
            self.setItem(rowIndex, 0, titleWidget)
            # 行值
            if  item.get('groupLine', False) == True:
                labelWidget = TableWidgetItemRowHeader(groupLine=True)
                self.setItem(rowIndex, 1, labelWidget)
            else:
                self.setCellWidget(rowIndex, 1, self._init_value_widget(item, rowIndex))
            # 增加行数
            rowIndex += 1
    def _clear(self):
        num_rows = self.rowCount()
        if num_rows > 0:
            for i in range(0, num_rows):
                # 移除该行的所有单元格和数据
                for column in range(self.columnCount()):
                    item = self.item(i, column)
                    if item is not None:
                        self.takeItem(i, column)
                    cell_widget = self.cellWidget(i, column)
                    if cell_widget is not None:
                        cell_widget.deleteLater()
            # self.clear()

    def _remove_rows_after(self, row):
        # 获取行数
        num_rows = self.rowCount()
        if row+1 <= num_rows:
            # 从指定行的下一行开始移除
            for i in range(row + 1, num_rows):
                # 移除该行的所有单元格和数据
                for column in range(self.columnCount()):
                    item = self.item(i, column)
                    if item is not None:
                        self.takeItem(i, column)
                    cell_widget = self.cellWidget(i, column)
                    if cell_widget is not None:
                        cell_widget.deleteLater()
        self.setRowCount(row+1)
    # 返回 当前显示行数据
    def _get(self):
        count = self.rowCount()
        data = {}
        for row in range(count):
            widgetItem = self.item(row,0)
            # 注意补充 title 的checkbox ，因此当可能存在 复选框时，之后勾选的内容才能返回
            if not self._check_row_title_widget_toggle(row) or self._row_title_is_checked(row) :
                bindData = widgetItem.data(QtCore.Qt.UserRole) or {}
                name = bindData.get('name')
                valueWidget = self.cellWidget(row, 1)
                if name and hasattr(valueWidget, '_get'):
                    value = valueWidget._get()
                    data[name] = value
        return data 
    def _init_value_widget(self, item, rowIndex):
        widgetType = item.get('type', 'text')
        name = item.get('name')
        widgetCell =  (widgetType and self.TYPES.get(widgetType) or self.TYPES.get('text'))(self)
        if hasattr(widgetCell, '_init'):
            widgetCell._init({'rowIndex':rowIndex, **item})
        # 初始化 表格信息
        # # 初始化绑定信息
        # # 初始化 数据
        # 更新值
        newValue = None
        if 'value' in item: # 不能直接使用  item.get('value', '') 是因为 value 可能为 0 如果值为0 会默认设置为 ''
            newValue = item.get('value')
        elif name in self.__cacheData:
            newValue = self.__cacheData.get(name) 
        self._update_row_title_checked(rowIndex, newValue != None)

        if newValue != None:
            widgetCell._set(newValue)

        # 如果是分组行 则右侧内容 TODO
        # if item.get('groupLine'):
        #     pass
        # 更新提示信息
        # placeholder = item.get('placeholder')
        # if placeholder:
        #     widgetCell.widget.setPlaceholderText(placeholder)
        return widgetCell
    # 修改缓存数据
    def change_cache_key_value(self, key, value):
        if not self._isInit: # 初始化时不更新缓存数据 防止有些默认数据影响实际值
            self.__cacheData[key] = value
    def get_cache_key_value(self, key):
        if key in self.__cacheData:
            return self.__cacheData.get(key)
        return None
    # 动态更新数据 注意 
    def _update_values(self, values:dict={}):
        count = self.rowCount()
        # 注意 有可能更新 表格或者 名称
        for row in range(count):
            widgetCell = self.cellWidget(row,1)
            if hasattr(widgetCell, 'data'):
                name = widgetCell.data.get('name')
                self._update_row_title_checked(row, values.get(name) != None)
                if name and values.get(name) != None:
                    widgetCell._set(values.get(name))
        if hasattr(self, 'parent') and hasattr(self.parent, 'onUpdateName') and 'name' in values:
            self.parent.onUpdateName(values.get('name'))
    
    # 由外部触发的更新 
    def update(self, data:dict={}):
        for key, value in data.items():
            self.__cacheData[key] = value
        self._update_values(data)

    # 判断指定行 的标题列 是否有checkbox
    def _check_row_title_widget_toggle(self, row):
        widgetItem = self.item(row,0)
        if widgetItem.flags() & QtCore.Qt.ItemIsUserCheckable:
            return True 
        return False
    # 判断指定行 的标题列 的checkbox 是否选中
    def _row_title_is_checked(self, row):
        widgetItem = self.item(row,0)
        return widgetItem.checkState() == QtCore.Qt.Checked
    # 更新行的标题列的checkbox 选中状态 在初始化时或者动态更新值时调用
    def _update_row_title_checked(self, row, checked):
        if self._check_row_title_widget_toggle(row):
            widgetItem = self.item(row,0)
            widgetItem.setCheckState(QtCore.Qt.Checked if checked else QtCore.Qt.Unchecked)
        
    